﻿namespace Cos.Module.Blog.NHibernate
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Cos.Core.Model.Blog;
    using Cos.Core.Model.Common;
    using Cos.Core.NHibernate;
    using Cos.Core.NHibernate.Util;
    using Cos.Module.Blog.Data;
    using Cos.Module.Blog.Exceptions;

    using global::NHibernate;
    using global::NHibernate.Criterion;

    /// <summary>
    /// Blog Repository
    /// </summary>
    public class BlogRepository : RepositoryBase, IBlogRepository
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BlogRepository"/> class.
        /// </summary>
        /// <param name="sessionFactory">The session factory.</param>
        public BlogRepository(ISessionFactory sessionFactory)
            : base(sessionFactory)
        {
        }

        /// <summary>
        /// Gets the list of tags.
        /// </summary>
        public IEnumerable<Tag> Tags
        {
            get
            {
                var criteria = this.CurrentSession.CreateCriteria<BlogTagDataModel>();
                criteria.AddOrder(Order.Asc("name"));

                return criteria.List<BlogTagDataModel>().Select(x => x.ToModel());
            }
        }

        /// <summary>
        /// Gets the blog by id.
        /// </summary>
        /// <param name="blogId">The blog id.</param>
        /// <param name="culture">The culture object.</param>
        /// <returns>
        /// The blog found
        /// </returns>
        /// <exception cref="BlogNotFoundException">If the blog could not be found or converted</exception>
        public Blog GetBlog(int blogId, string cultureCode)
        {
            // Get the data blog the database
            var blog = this.CurrentSession.Get<BlogDataModel>(blogId);

            if (blog == null)
            {
                throw new BlogNotFoundException("Not such blog in the database. Blog.ID: " + blogId);
            }

            var cultureDataModel = this.CurrentSession.Get<CultureDataModel>(cultureCode);

            if (cultureDataModel == null)
            {
                throw new BlogNotFoundException("Not such blog in the database. Culture.CultureCode: " + cultureCode);
            }

            // Get number of articles
            // int numberOfArticles = this.GetNumberOfArticles(blogId);
            return blog.ToBlog(cultureCode);
        }

        public Blog GetBlog(int blogId, Culture culture)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Used to retrieve the number of articles of a blog.
        /// </summary>
        /// <param name="blogId">The blog id.</param>
        /// <returns>
        /// The number of articles
        /// </returns>
        public int GetNumberOfArticles(int blogId)
        {
            var sqlQuery = this.CurrentSession.CreateSQLQuery("SELECT count(*) from article where BlogId = ?");
            sqlQuery.SetInt32(0, blogId);

            return (int)sqlQuery.UniqueResult();
        }

        public Article GetArticle(int articleId, Culture culture)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Article> GetArticles(int blogId, Culture culture, int firstResult, int maxResult, long toDate)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the article by id.
        /// </summary>
        /// <param name="articleId">The article id.</param>
        /// <param name="culture">The article culture to grab.</param>
        /// <returns>
        /// The article
        /// </returns>
        /// <exception cref="ArticleNotFoundException">if the article could not be found or converted</exception>
        public Article GetArticle(int articleId, string cultureCode)
        {
            return this.CurrentSession.Get<ArticleDataModel>(articleId).ToArticle(cultureCode);
        }

        /// <summary>
        /// Gets the articles associated to a blog.
        /// </summary>
        /// <param name="blogId">The blog id.</param>
        /// <param name="culture">The culture code.</param>
        /// <param name="firstResult">Tell to start at the firstResult id.</param>
        /// <param name="maxResult">The maximum articles to retrieve.</param>
        /// <param name="toDate">The maxResult articles until the toDate is reached.</param>
        /// <returns>
        /// articles of the blog
        /// </returns>
        /// <exception cref="ArticleNotFoundException">if the article could not be found or converted</exception>
        public IEnumerable<Article> GetArticles(int blogId, string cultureCode, int firstResult, int maxResult, long toDate)
        {
            var criteria = this.CurrentSession.CreateCriteria<ArticleDataModel>();

            // Set the maximum of article to retrieve
            if (maxResult != -1)
            {
                criteria.SetMaxResults(maxResult);
            }

            criteria.SetFirstResult(firstResult);
            criteria.Add(Restrictions.Eq("blog.id", blogId));

            if (toDate != long.MaxValue)
            {
                criteria.Add(Restrictions.Gt("creationDate", new DateTime(toDate)));
            }

            criteria.AddOrder(Order.Desc("creationDate"));

            var articles = criteria.List<ArticleDataModel>();

            var cultureDataModel = this.CurrentSession.Get<CultureDataModel>(cultureCode);

            if (cultureCode == null)
            {
                throw new ArticleNotFoundException("Not such articles in the database. Culture.CultureCode: " + cultureCode);
            }

            return articles.Select(x => x.ToArticle(cultureCode));
        }

        /// <summary>
        /// Updates the article.
        /// </summary>
        /// <param name="articleVo">The article.</param>
        public void UpdateArticle(Cos.Core.Model.Blog.Article articleVo)
        {
            var blogArticleCulture = this.CurrentSession.Get<ArticleCultureDataModel>(articleVo.Id);

            // update it
            blogArticleCulture.Title = articleVo.Title;
            blogArticleCulture.Text = articleVo.Text;

            this.CurrentSession.Save(blogArticleCulture);
        }

        /// <summary>
        /// Submits an article.
        /// </summary>
        /// <param name="blogId">The blog id.</param>
        /// <param name="cultureCode">The culture code.</param>
        /// <param name="article">The article.</param>
        public void SubmitArticle(int blogId, Cos.Core.Model.Common.Culture cultureCode, Cos.Core.Model.Blog.Article article)
        {
            // Get the dependen objects from the data base
            var culture = this.CurrentSession.Get<CultureDataModel>(cultureCode.CultureCode);

            if (culture == null)
            {
                throw new NotSupportedException("Unable to save an article. The culture is empty");
            }

            var blogArticleCultures = new HashSet<ArticleCultureDataModel>();

            var blogArticle = new ArticleDataModel
                                               {
                                                   CreationDate = DateTime.Now,
                                                   BlogId = this.CurrentSession.Get<BlogDataModel>(blogId).Id
                                               };



            var blogArticleCulture = new ArticleCultureDataModel
                                                             {
                                                                 Title = article.Title,
                                                                 Text = article.Text,
                                                                 AuthorId = article.AuthorId,
                                                                 AuthorFirstName = article.AuthorFirstName,
                                                                 AuthorLastName = article.AuthorLastName,
                                                                 CultureCode = article.CultureCode,
                                                                 LastModified = DateTime.Now,
                                                                 ArticleId = blogArticle.Id
                                                             };

            //blogArticle.ArticleCultures = blogArticleCultures;
            blogArticleCultures.Add(blogArticleCulture);

            // Add the tags TODO
            //ISet<BlogTagDataModel> blogTags = new HashSet<BlogTagDataModel>();

            //foreach (var tag in article.Tags)
            //{
            //    blogTags.Add(this.CurrentSession.Get<BlogTagDataModel>(tag.Id));
            //}

            this.CurrentSession.Save(blogArticle);
        }

        /// <summary>
        /// Deletes an article.
        /// </summary>
        /// <param name="articleId">The article id.</param>
        public void DeleteArticle(int articleId)
        {
            // Retrieve the article culture
            var blogArticleCulture = this.CurrentSession.Get<ArticleCultureDataModel>(articleId);

            // FIXME Test and ensure cascade to blog-culture
            this.CurrentSession.Delete(blogArticleCulture.ArticleId);
        }

        /// <summary>
        /// Submit a comment.
        /// </summary>
        /// <param name="culture">The culture.</param>
        /// <param name="comment">The comment.</param>
        public void SubmitComment(Culture culture, Comment comment)
        {
            var blogArticle = this.CurrentSession.Get<ArticleCultureDataModel>(comment.ArticleId);

            var blogArticleComment = new CommentDataModel();
            blogArticleComment.ArticleCultureId = blogArticle.Id;
            blogArticleComment.CreationDate = DateTime.Now;
            blogArticleComment.LastModified = DateTime.Now;
            blogArticleComment.Text = comment.Text;
            blogArticleComment.AuthorId = comment.AuthorId;
            blogArticleComment.AuthorFirstName = comment.AuthorFirstName;
            blogArticleComment.AuthorLastName = comment.AuthorLastName;

            // TODO
            //CosValidatorHelper.ensureValid(blogArticleComment);

            this.CurrentSession.SaveOrUpdate(blogArticleComment);
        }

        /// <summary>
        /// Deletes the comment.
        /// </summary>
        /// <param name="commentId">The comment id.</param>
        public void DeleteComment(int commentId)
        {
            // Retrieve the article comment
            var articleComment = this.CurrentSession.Get<CommentDataModel>(commentId);

            // Delete
            this.CurrentSession.Delete(articleComment);
        }
    }
}
